/**
  ******************************************************************************
  * @file    sw_spi.c 
  * @brief   This file realizes the software simulation IIC driver library.
  ******************************************************************************
  */


#include "sw_spi.h"

#include <string.h>

/*******************************************************************************
 * spi physical interface
 ******************************************************************************/
// SDO pin output low level
#define SPI_SDO_LOW(p_dev)          p_dev->ops.sdo_low()

// SDO pin output high level
#define SPI_SDO_HIGH(p_dev)         p_dev->ops.sdo_high()

// SCL pin output low level
#define SPI_SCK_LOW(p_dev)          p_dev->ops.sck_low()

// SCL pin output high level
#define SPI_SCK_HIGH(p_dev)         p_dev->ops.sck_high()

// Read SDI pin level
#define SPI_SDI_READ(p_dev)         p_dev->ops.sdi_read_level()

// CS pin output low level
#define SPI_CS_LOW(p_dev)           p_dev->ops.cs_low()

// CS pin output high level
#define SPI_CS_HIGH(p_dev)          p_dev->ops.cs_high()

/*******************************************************************************
 * linked list function
 ******************************************************************************/
// Insert node into linked list
#define INSERT_INTO(node) do {                          \
                              node->next = head_handle; \
                              head_handle = node;       \
                          } while (0)
                        
/* Private variables ---------------------------------------------------------*/
#if (SPI_DEV_FIND > 0u)
static struct sw_spi_dev* head_handle = NULL;
#endif

/* Private macro -------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/

// 根据模式获取时钟极性
static uint8_t get_cpol(SPI_Mode_t mode) {
    return (mode == SPI_MODE_2 || mode == SPI_MODE_3);
}

// 根据模式获取采样边沿
static uint8_t get_cpha(SPI_Mode_t mode) {
    return (mode == SPI_MODE_1 || mode == SPI_MODE_3);
}

// 反转字节位序（用于LSB_FIRST）
static uint8_t reverse_byte(uint8_t b) {
    b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
    b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
    b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
    return b;
}


/**
 * @brief  Query device baseed on driver name
 * @param  dev_name : Pointer to driver name
 * @return spi object pointer
 */
#if (SPI_DEV_FIND > 0u)
struct sw_spi_dev *sw_spi_dev_find(const char* name)
{
    struct sw_spi_dev* TmpCell;

    /*! Query all nodes in the linked list */
    for(TmpCell = head_handle; TmpCell != NULL; TmpCell = TmpCell->next) {
        /*! Match driver name */
        if (strcmp(TmpCell->name, name) == 0) {
            return TmpCell;
        }
    }
    return NULL;
}
#endif

/**
 * @brief  Initialization spi
 * @param  dev : Pointer to iic structure
 * @return none
 */
void sw_spi_init(struct sw_spi_dev *dev)
{
    // Initialzation pin high level    
    SPI_SDO_HIGH(dev);
    SPI_CS_HIGH(dev);
    if(get_cpol(dev->mode)) 
        SPI_SCK_HIGH(dev);
    else
        SPI_SCK_LOW(dev);


    #if (SPI_DEV_FIND > 0u)
    // Insert node into linked list
    INSERT_INTO(dev);
    #endif
}

uint8_t sw_spi_transfer_byte(struct sw_spi_dev *dev, uint8_t byte)
{
    uint8_t rx_byte = 0;
    uint8_t cpol = get_cpol(dev->mode);
    uint8_t cpha = get_cpha(dev->mode);

    if(dev->bit_order == SPI_LSB_FIRST) {
        byte = reverse_byte(byte);
    }

    for (uint8_t i = 0; i < 8; i++) {

        // MOSI output
        if(byte & 0x80) {
            SPI_SDO_HIGH(dev);
        }
        else {
            SPI_SDO_LOW(dev);
        }

        if(!cpha) dev->delay_us(dev->speed);

        // SCK output
        if(!cpol)
            SPI_SCK_HIGH(dev);
        else    
            SPI_SCK_LOW(dev);
        
        // SDI input
        if(cpha) {
            rx_byte <<= 1;
            if(SPI_SDI_READ(dev)) {
                rx_byte |= 0x01;
            }
        }
        dev->delay_us(dev->speed);

        // SCK output
        if(cpol)
            SPI_SCK_HIGH(dev);
        else    
            SPI_SCK_LOW(dev);
        
        if(!cpha) {
            rx_byte <<= 1;
            if(SPI_SDI_READ(dev)) {
                rx_byte |= 0x01;
            }
        }
        dev->delay_us(dev->speed);

        byte <<= 1;
    }

    if(dev->bit_order == SPI_LSB_FIRST) {
        rx_byte = reverse_byte(rx_byte);
    }
    return rx_byte;
}


void sw_spi_transfer(struct sw_spi_dev *dev, sw_spi_msg_t *msgs)
{
    uint8_t tx_byte;
    uint8_t rx_byte;
    SPI_CS_LOW(dev);  // 拉低片选信号
    for(int i = 0; i < msgs->len; i++) {
        uint8_t tx_byte = msgs->buf[i] ? msgs->buf[i] : 0xFF;  // 支持纯接收模式，如果发送数据为空，则发送0xFF
        rx_byte = sw_spi_transfer_byte(dev, msgs->buf[i]);  // 发送数据并接收数据
        // msgs->buf[i] = rx_byte;  
    }
    SPI_CS_HIGH(dev);  // 拉高片选信号
}